package mgrhttp

import (
	"encoding/json"
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"strconv"

	"log"
	"os"
	"path/filepath"
	"strings"

	config "csudata.com/zqpool/src/config"
	"csudata.com/zqpool/src/csulog"
	poolserver "csudata.com/zqpool/src/poolserver"
	"go.uber.org/zap"
)

const ApiSuccess = 0
const ApiBadRequest = 1
const ApiSessionTimeOut = 2
const ApiServerError = -1

func httpRetMsg(httpResW http.ResponseWriter, Code int, Message string) {
	httpResW.Write([]byte(fmt.Sprintf(`{"code": %d, "message": "%s"}`, Code, Message)))
}

func httpRetData(httpResW http.ResponseWriter, Code int, DataStr string) {
	httpResW.Write([]byte(fmt.Sprintf(`{"code": %d, "data": %s}`, Code, DataStr)))
}

func httpHandlePoolAddBeDb(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolName string
	var dbPortal string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	// ck, err := r.Cookie("sessionid")
	// if err != nil {
	// 	w.Write([]byte(fmt.Sprintf(`{"code":%d, "message": %s}`, ApiSessionTimeOut, "session timeout ")))
	// 	return
	// }
	// fmt.Printf("ck=%s\n", ck.Value)

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		dbPortal = r.Form.Get("db_portal")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		dbPortal = r.PostForm.Get("db_portal")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	portals := strings.Split(dbPortal, ",")
	for _, portal := range portals {
		err = poolserver.PoolAddHost(poolName, portal)
		if err != nil {
			httpRetMsg(w, ApiBadRequest, err.Error())
			return
		}
	}
	msg := fmt.Sprintf("Add portal(%s) to pool(%s) success.", dbPortal, poolName)
	httpRetMsg(w, ApiSuccess, msg)
}

func httpHandlePoolRemoveBeDb(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolName string
	var dbPortal string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	// ck, err := r.Cookie("sessionid")
	// if err != nil {
	// 	w.Write([]byte(fmt.Sprintf(`{"code":%d, "message": %s}`, ApiSessionTimeOut, "session timeout ")))
	// 	return
	// }
	// fmt.Printf("ck=%s\n", ck.Value)

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		dbPortal = r.Form.Get("db_portal")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		dbPortal = r.PostForm.Get("db_portal")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	pool := poolserver.GetPoolByName(poolName)
	if pool == nil {
		msg := fmt.Sprintf("The pool(%s) not exists.", poolName)
		httpRetMsg(w, ApiBadRequest, msg)
		return
	}

	portals := strings.Split(dbPortal, ",")
	for _, portal := range portals {
		err = pool.RemoveHost(portal)
		if err != nil {
			httpRetMsg(w, ApiBadRequest, err.Error())
			return
		}
	}
	msg := fmt.Sprintf("Remove portal(%s) from pool(%s) success.", dbPortal, poolName)
	httpRetMsg(w, ApiSuccess, msg)
}

func httpHandlePoolListBeDb(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolName string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	bePoolStatusList, err := poolserver.PoolGetBeDbStatusList(poolName)
	if err != nil {
		httpRetMsg(w, ApiBadRequest, err.Error())
	} else {
		strList, _ := json.Marshal(bePoolStatusList)
		httpRetData(w, ApiSuccess, string(strList))
	}
}

func httpHandlePoolModifyFeInfo(w http.ResponseWriter, r *http.Request) {
	var err error
	var FeUser string
	var FePasswd string
	var FeDbName string
	var poolName string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		FeUser = r.Form.Get("fe_user")
		FePasswd = r.Form.Get("fe_passwd")
		FeDbName = r.Form.Get("fe_dbname")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		FeUser = r.PostForm.Get("fe_user")
		FePasswd = r.PostForm.Get("fe_passwd")
		FeDbName = r.PostForm.Get("fe_dbname")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	// check the pool is exists or not
	pool := poolserver.GetPoolByName(poolName)
	if pool == nil {
		msg := fmt.Sprintf("The pool(%s) not exists.", poolName)
		httpRetMsg(w, ApiBadRequest, msg)
		return
	}

	code, result := pool.ModifyFeInfo(FeUser, FePasswd, FeDbName, poolName)
	if code != 0 {
		httpRetMsg(w, ApiBadRequest, result)
		return
	}
	httpRetMsg(w, ApiSuccess, result)
}

func httpHandlePoolModifyBeInfo(w http.ResponseWriter, r *http.Request) {
	var err error
	var BeUser string
	var BePasswd string
	var poolName string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		BeUser = r.Form.Get("be_user")
		BePasswd = r.Form.Get("be_passwd")
		// BeDbName = r.Form.Get("be_dbname")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		BeUser = r.PostForm.Get("be_user")
		BePasswd = r.PostForm.Get("be_passwd")
		// BeDbName = r.PostForm.Get("be_dbname")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	pool := poolserver.GetPoolByName(poolName)
	if pool == nil {
		msg := fmt.Sprintf("The pool(%s) is not exists.", poolName)
		httpRetMsg(w, ApiBadRequest, msg)
		return
	}

	code, result := pool.ModifyBeInfo(BeUser, BePasswd)
	httpRetMsg(w, code, result)
}

func httpHandlePoolGetPoolList(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolName string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	// get the pool
	code, poolList := poolserver.GetPoolList(poolName)
	if code != 0 {
		msg := fmt.Sprintf("Cant get any records for the pool(%s).", poolName)
		httpRetMsg(w, ApiBadRequest, msg)
		return
	}

	retList, _ := json.Marshal(poolList)
	httpRetData(w, ApiSuccess, string(retList))
}

func httpHandlePoolAddPool(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolID string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolID = r.Form.Get("pool_id")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolID = r.PostForm.Get("pool_id")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	var exePath string
	var configFile string
	exeFile, err := os.Executable()
	if err != nil {
		log.Printf("Get the exe file failed: %s\n", err.Error())
		msg := fmt.Sprintf("Add pool with unexpected error %s.", err.Error())
		httpRetMsg(w, ApiBadRequest, msg)
		return
	}
	exePath = filepath.Dir(exeFile)
	configFile = filepath.Join(exePath, "zqpool.conf")

	//
	err, tem_pool := poolserver.GetNewPool(configFile, poolID)
	if err != nil {
		log.Printf("Get the new pool from %s failed: %s\n", configFile, err.Error())
		msg := fmt.Sprintf("Get the new pool from %s failed: %s.", configFile, err.Error())
		httpRetMsg(w, ApiBadRequest, msg)
		return
	}

	// test add conn
	poolserver.PoolsLock.Lock()
	for portal, bePool := range tem_pool.BePools {
		err := poolserver.BuildBePoolConn(tem_pool, portal, bePool)
		if err != nil {
			poolserver.PoolsLock.Unlock()
			log.Printf("Build pool backend connect failed: %s", err.Error())
			msg := fmt.Sprintf("PoolServer build pool conn failed for the pool(id=%s).", poolID)
			httpRetMsg(w, ApiBadRequest, msg)
			return
		}
		bePool.State = poolserver.BE_ONLINE
	}
	poolserver.PoolsLock.Unlock()
	poolserver.AddPool(tem_pool)
	log.Printf("Add pool(poolID=%s) success.", poolID)
	msg := fmt.Sprintf("PoolServer add pool(ID=%s) success.", poolID)
	httpRetMsg(w, ApiSuccess, msg)
}

func httpHandlePoolRemovePool(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolName string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	err = poolserver.RemovePool(poolName)
	if err != nil {
		log.Printf("Remove pool(poolName=%s) failed: %s", poolName, err.Error())
		httpRetMsg(w, ApiBadRequest, fmt.Sprintf("Remove the pool(%s) failed, %s.", poolName, err.Error()))
		return
	}

	log.Printf("Remove pool(poolName=%s) success.", poolName)
	msg := fmt.Sprintf("Remove the pool(%s) success.", poolName)
	httpRetMsg(w, ApiSuccess, msg)
}

func httpHandlePoolModifyConns(w http.ResponseWriter, r *http.Request) {
	var err error
	var poolName string
	var fe_max_conns string
	var be_rw_conns string
	var be_rd_conns string
	var mgr_token string
	var ConfigToken string

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		poolName = r.Form.Get("pool_name")
		fe_max_conns = r.Form.Get("fe_max_conns")
		be_rw_conns = r.Form.Get("be_rw_conns")
		be_rd_conns = r.Form.Get("be_rd_conns")
		mgr_token = r.Form.Get("mgr_token")
	} else {
		poolName = r.PostForm.Get("pool_name")
		fe_max_conns = r.PostForm.Get("fe_max_conns")
		be_rw_conns = r.PostForm.Get("be_rw_conns")
		be_rd_conns = r.PostForm.Get("be_rd_conns")
		mgr_token = r.PostForm.Get("mgr_token")
	}

	// check token
	ConfigToken = config.Get("mgr_token")
	if ConfigToken == "" || ConfigToken != mgr_token {
		httpRetMsg(w, ApiBadRequest, "Forbidden the token is error.")
		return
	}

	// modify the number of pool connections
	code, result := poolserver.ModifyPoolConns(poolName, fe_max_conns, be_rw_conns, be_rd_conns)
	if code != 0 {
		httpRetMsg(w, ApiBadRequest, result)
		return
	}
	httpRetMsg(w, ApiSuccess, result)
}

func httpHandleSetLogLevel(w http.ResponseWriter, r *http.Request) {
	var loglevel string
	var err error

	if err = r.ParseForm(); err != nil {
		httpRetMsg(w, ApiBadRequest, strconv.Quote(err.Error()))
		return
	}

	if r.Method == "GET" {
		loglevel = r.Form.Get("loglevel")
	} else {
		loglevel = r.PostForm.Get("loglevel")
	}

	err = csulog.SetLogger(loglevel)
	if err != nil {
		log.Printf("set log level failed: %s", err.Error())
		httpRetMsg(w, ApiBadRequest, err.Error())
		return
	}

	log.Printf("set log level success.")
	msg := fmt.Sprintf("set log level to %s success.", loglevel)
	httpRetMsg(w, ApiSuccess, msg)
}

func httpHandleGetLogLevel(w http.ResponseWriter, r *http.Request) {
	var loglevel string

	loglevel = csulog.GetLogger()
	msg := fmt.Sprintf("Current log level is %s.", loglevel)
	httpRetMsg(w, ApiSuccess, msg)
}

func StartHttpServer() {

	var err error

	webPort := config.GetInt("mgr_port")
	var httpListenAddr = fmt.Sprintf(":%d", webPort)
	http.HandleFunc("/api/v1/pool_add_pool", httpHandlePoolAddPool)
	http.HandleFunc("/api/v1/pool_remove_pool", httpHandlePoolRemovePool)
	http.HandleFunc("/api/v1/pool_list_be_db", httpHandlePoolListBeDb)
	http.HandleFunc("/api/v1/pool_add_be_db", httpHandlePoolAddBeDb)
	http.HandleFunc("/api/v1/pool_remove_be_db", httpHandlePoolRemoveBeDb)
	http.HandleFunc("/api/v1/pool_modify_fe_info", httpHandlePoolModifyFeInfo)
	http.HandleFunc("/api/v1/pool_modify_be_info", httpHandlePoolModifyBeInfo)
	http.HandleFunc("/api/v1/pool_get_pool_list", httpHandlePoolGetPoolList)
	http.HandleFunc("/api/v1/pool_modify_conns", httpHandlePoolModifyConns)
	http.HandleFunc("/api/v1/set_log_level", httpHandleSetLogLevel)
	http.HandleFunc("/api/v1/get_log_level", httpHandleGetLogLevel)

	zap.S().Infof("Manager http Listen %s", httpListenAddr)
	err = http.ListenAndServe(httpListenAddr, nil)
	if err != nil {
		zap.S().Error("Manager http listen error : ", err)
	}
}
